
#include "tools_object_transform.h"
#include "pmodel.h"
#include "3d_primitives.h"

using namespace WtfEngine;


WtfGraphics::GraphicsData::tCacheRef __GetScaleWidgetGfx() {
	static WtfGraphics::Model::tRef rGfx;
	if(rGfx.isNull()) {
		rGfx = new WtfGraphics::Model("scaleWidgetGfx");
		WtfGraphics::Primitives::Oblong::tRef rArrow = new WtfGraphics::Primitives::Oblong(20, 1, 1);

		rGfx->InsertMesh(new WtfGraphics::TransformedMesh(rArrow, tVector(-10, 0, 0), Orientation3D::identity()));
		rGfx->InsertMesh(new WtfGraphics::TransformedMesh(rArrow, tVector(0, 0, 10),
			Orientation3D::Rotation(tVector(0, 1, 0), Angle(M_PI / 2))));
		rGfx->InsertMesh(new WtfGraphics::TransformedMesh(rArrow, tVector(0, 10, 0),
			Orientation3D::Rotation(tVector(0, 0, 1), Angle(M_PI / 2))));
	};
	return rGfx;
};

PhysicalModel::tCacheRef __GetScaleWidgetModel() {
	static PhysicalModels::Compound::tRef rModel;
	if(rModel.isNull()) {
		rModel = new PhysicalModels::Compound("");
		PhysicalModels::Oblong::tRef rArrow = new PhysicalModels::Oblong("", tVector(20, 1, 1));

		rModel->InsertModel(rArrow, tVector(-10, 0, 0), Orientation3D::identity());
		rModel->InsertModel(rArrow, tVector(0, 0, 10),
			Orientation3D::Rotation(tVector(0, 1, 0), Angle(M_PI / 2)));
		rModel->InsertModel(rArrow, tVector(0, 10, 0),
			Orientation3D::Rotation(tVector(0, 0, 1), Angle(M_PI / 2)));
	};
	return rModel;
};


ObjectTool::ObjectTool(const ObjectSelectionTool::tRef& rSel)
: WorldObject(WorldObject::tCreationOptions(), __GetScaleWidgetGfx(), __GetScaleWidgetModel(),
			  btRigidBody::btRigidBodyConstructionInfo(0, NULL, __GetScaleWidgetModel()->getCollisionShape())),
  mrSelection(rSel)
{
	// Disable collision response
	getBody()->setCollisionFlags(getBody()->getCollisionFlags() |
		btCollisionObject::CF_NO_CONTACT_RESPONSE);
};

void ObjectTool::Init() {
	IDynamic::Init();

	Kernel::GetInstance().GetGameState()->BindEvent(new ObjectMouseDownEvent(this, tVector()),
		new ObjFunctor<ObjectTool>(this, &ObjectTool::OnMouseDown));
	Kernel::GetInstance().GetGameState()->BindEvent(new MouseMoveEvent(),
		new ObjFunctor<ObjectTool>(this, &ObjectTool::OnMouseMove));
};

void ObjectTool::Destroy() {
	IDynamic::Destroy();
	WorldObject::Destroy();
};

void ObjectTool::Update() {
	tPosition pos;
	int n = 0;
	for(ObjectSelectionTool::tObjectList::const_iterator i = mrSelection->getSelection().begin();
		i != mrSelection->getSelection().end(); i++)
	{
		if(*i == this) continue;

		if(n == 0) {
			pos = (*i)->getPosition();
		} else {
			pos = ((pos * n) + (*i)->getPosition()) / (n + 1);
		};
		n++;
	};

	this->setPosition(pos);
};

void ObjectTool::OnMouseDown() {
	ObjectMouseDownEvent * pEv = Kernel::GetInstance().GetCurrentEvent<ObjectMouseDownEvent>();

	tScalar x = abs(pEv->getRelativePosition()[0]), y = abs(pEv->getRelativePosition()[1]),
		z = abs(pEv->getRelativePosition()[2]);
	miDragAxis = (x > y ? (x > z ? 0 : 2) : (y > z ? 1 : 2));

	Kernel::GetInstance().SetEventStatus(Event::STOPPED);
	InputHandler::GetInstance().CaptureAllInput(true);

	Kernel::GetInstance().BindEvent(new MouseUpEvent(),
		EventHandler(new ObjFunctor<ObjectTool>(this, &ObjectTool::OnMouseUp), EventHandler::PRIO_WM_HIGH));
};

void ObjectTool::OnMouseUp() {
	assert(miDragAxis != -1);
	miDragAxis = -1;
	Kernel::GetInstance().SetEventStatus(Event::STOPPED);
	Kernel::GetInstance().RemoveHandler(new ObjFunctor<ObjectTool>(this, &ObjectTool::OnMouseUp));
	InputHandler::GetInstance().CaptureAllInput(false);
};

void ObjectTool::OnMouseMove() {
	
};



/**** Move tool ****/

ObjectMoveTool::ObjectMoveTool(const ObjectSelectionTool::tRef &r)
: ObjectTool(r) {
};

void ObjectMoveTool::OnMouseMove() {
	MouseMoveEvent * pEv = Kernel::GetInstance().GetCurrentEvent<MouseMoveEvent>();

	if(miDragAxis >= 0) {
		tVector vDelta;
		vDelta[miDragAxis] = -((tScalar)pEv->GetDeltaY() * sqrt(abs((tScalar)pEv->GetDeltaY())) / 500);

		for(ObjectSelectionTool::tObjectList::const_iterator i = mrSelection->getSelection().begin();
			i != mrSelection->getSelection().end(); i++)
		{
			if(*i == this) continue;
			(*i)->setPosition((*i)->getPosition() + vDelta);
		};
	};
};



/**** Scale tool ****/

ObjectScaleTool::ObjectScaleTool(const ObjectSelectionTool::tRef &r)
: ObjectTool(r) {
};

void ObjectScaleTool::OnMouseMove() {
	MouseMoveEvent * pEv = Kernel::GetInstance().GetCurrentEvent<MouseMoveEvent>();

	if(miDragAxis >= 0) {
		tVector vDelta;
		vDelta[miDragAxis] = -((tScalar)pEv->GetDeltaY());

		WtfGraphics::TransformedMesh * pMesh;

		for(ObjectSelectionTool::tObjectList::const_iterator i = mrSelection->getSelection().begin();
			i != mrSelection->getSelection().end(); i++)
		{
			if(*i == this) continue;

			pMesh = dynamic_cast<WtfGraphics::TransformedMesh*>(&*(*i)->getGraphicsData());
			if(!pMesh) {
				pMesh = new WtfGraphics::TransformedMesh((*i)->getGraphicsData(),
					tVector(), Orientation3D::identity());
				(*i)->setGraphicsData(pMesh);
			};

			pMesh->setTransform(pMesh->getTransform().then(Transformation3D::scale(vDelta[miDragAxis] + 1)));
		};
	};
};
